﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq.Expressions;

namespace SmartClientPlatform.RemoteClientBase
{
    public interface IDataObject<TData>
    {
        TResult GetValue<TResult>(Expression<Func<TData, TResult>> propertyExpression);
        void SetValue<TResult>(Expression<Func<TData, TResult>> propertyExpression, TResult value);

        IDataObject<TResult> GetRef<TResult>(Expression<Func<TData, TResult>> propertyExpression);
        void SetRef<TResult>(Expression<Func<TData, TResult>> propertyExpression, IDataObject<TResult> value);

        IEnumerable<IDataObject<TResult>> GetValue<TResult>(
            Expression<Func<TData, EntitySet<TResult>>> propertyExpression) where TResult : class;
    }

    public class EditContext
    {
        private readonly Stack<Action> stack = new Stack<Action>();
        internal void AddAction(Action action)
        {
            stack.Push(action);
        }

        public void Undo()
        {
            while (stack.Count > 0)
            {
                stack.Pop()();
            }
        }
    }

    public static class EditDataObjectUtil
    {
        public static IDataObject<TData> CoupleEditContext<TData>(
            this IDataObject<TData> dataObject,
            EditContext editContext)
        {
            return new DataObjectUndoer<TData>(dataObject, editContext);
        }

        private class DataObjectUndoer<TData> : IDataObject<TData>
        {
            private readonly IDataObject<TData> dataObject;
            private readonly EditContext context;

            public DataObjectUndoer(IDataObject<TData> dataObject, EditContext context)
            {
                this.dataObject = dataObject;
                this.context = context;
            }

            public TResult GetValue<TResult>(Expression<Func<TData, TResult>> propertyExpression)
            {
                return dataObject.GetValue(propertyExpression);
            }

            public void SetValue<TResult>(Expression<Func<TData, TResult>> propertyExpression, TResult value)
            {
                var oldValue = dataObject.GetValue(propertyExpression);
                context.AddAction(() => dataObject.SetValue(propertyExpression, oldValue));

                dataObject.SetValue(propertyExpression, value);
            }

            public IDataObject<TResult> GetRef<TResult>(Expression<Func<TData, TResult>> propertyExpression)
            {
                throw new NotImplementedException();
            }

            public void SetRef<TResult>(Expression<Func<TData, TResult>> propertyExpression, IDataObject<TResult> value)
            {
                throw new NotImplementedException();
            }

            public IEnumerable<IDataObject<TResult>> GetValue<TResult>(Expression<Func<TData, EntitySet<TResult>>> propertyExpression) where TResult : class
            {
                throw new NotImplementedException();
            }
        }
    }
}
